package com.example.demo.loader;

import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import org.springframework.boot.system.ApplicationHome;

import javax.tools.*;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 加载器
 */
public class MemoryClassLoader extends URLClassLoader {

	private static final Map<String, byte[]> CLASS_BYTES = new ConcurrentHashMap<>();

	private MemoryClassLoader() {
		super(new URL[0], MemoryClassLoader.class.getClassLoader());
	}

	/**
	 * 生成新的实例
	 */
	public static MemoryClassLoader genInstance(){
		return new MemoryClassLoader();
	}

	/**
	 * 注册Java 字符串到内存类加载器中
	 * @param className 类名字
	 * @param javaStr   Java字符串
	 */
	public void registerJava(String className, String javaStr) {
		try {
			Map<String, byte[]> map = compile(className, javaStr);
			if (map != null) {
				CLASS_BYTES.putAll(map);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 自定义Java文件管理器
	 * @param var1 /
	 * @param var2 /
	 * @param var3 /
	 * @return /
	 */
	public static SpringJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> var1, Locale var2, Charset var3) {
		Context var4 = new Context();
		var4.put(Locale.class, var2);
		if (var1 != null) {
			var4.put(DiagnosticListener.class, var1);
		}
		PrintWriter var5 = var3 == null ? new PrintWriter(System.err, true) : new PrintWriter(new OutputStreamWriter(System.err, var3), true);
		var4.put(Log.outKey, var5);
		return new SpringJavaFileManager(var4, true, var3);
	}

	/**
	 * 编译Java代码
	 * @param className 类名字
	 * @param javaStr   Java代码
	 * @return class 二进制
	 */
	private static Map<String, byte[]> compile(String className, String javaStr) {
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager stdManager = getStandardFileManager(null, null, null);
		try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) {
			JavaFileObject javaFileObject = manager.makeStringSource(className, javaStr);
			JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Arrays.asList(javaFileObject));
			Boolean result = task.call();
			if (result != null && result) {
				return manager.getClassBytes();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] buf = CLASS_BYTES.get(name);
		if (buf == null) {
			return super.findClass(name);
		}
		CLASS_BYTES.remove(name);
		return defineClass(name, buf, 0, buf.length);
	}

	/**
	 * 开放findClass 给外部使用
	 * @param name classname
	 * @return class对象
	 */
	public Class<?> getClass(String name) throws ClassNotFoundException {
		return this.findClass(name);
	}

	/**
	 * 获取jar包所在路径
	 * @return /
	 */
	public static String getPath() {
		ApplicationHome home = new ApplicationHome(MemoryJavaFileManager.class);
		return home.getSource().getPath();
	}

	/**
	 * 判断是否jar模式运行
	 * @return boolean
	 */
	public static boolean isJar() {
		return getPath().endsWith(".jar");
	}
}
